package com.culture.bootdemo.utils;

import org.apache.commons.lang3.StringUtils;

/**
 * @author wen
 * @date 2019/11/13 17:38
 * @Description 唯一ID生成器-雪花算法
 **/
public class IdWorker {

    private final long workerId;

    private final static long TWEPOCH = 1361753741828L;

    private long sequence = 0L;

    private final static long WORKER_ID_BITS = 4L;

    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

    private final static long SEQUENCE_BITS = 10L;

    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;

    private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);

    private long lastTimestamp = -1L;


    private static final IdWorker ID_WORKER = new IdWorker(1);

    /**
     * 对外方法: 获取唯一id
     *
     * @return
     */
    public static long getId() {
        return ID_WORKER.nextId();
    }

    /**
     * 对外方法：获取唯一id
     *
     * @return
     */
    public static String getIdStr() {
        return String.valueOf(ID_WORKER.nextId());
    }


    private IdWorker(final long workerId) {
        super();
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("Id不能大于 %d 或小于 0", MAX_WORKER_ID));
        }
        this.workerId = workerId;
    }


    private synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & SEQUENCE_MASK;
            if (this.sequence == 0) {
                System.out.println("SEQUENCE_MASK --> " + SEQUENCE_MASK);
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }
        if (timestamp < this.lastTimestamp) {
            try {
                throw new Exception(String.format("时钟倒退。拒绝生成id %d 毫秒", this.lastTimestamp - timestamp));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        this.lastTimestamp = timestamp;
        long nextId = ((timestamp - TWEPOCH << TIMESTAMP_LEFT_SHIFT)) | (this.workerId << WORKER_ID_SHIFT) | (this.sequence);
        // System.out.println("时间戳:" + timestamp + ", 时间戳左移:" + timestampLeftShift + ", 下一个Id:" + nextId + ", workerId:" + workerId + ", 序列:" + sequence);
        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        String idStr = getIdStr();
        System.out.println(idStr);
        System.out.println(idStr.length());
        System.out.println(idStr.length() <= 12 ? idStr : idStr.substring(idStr.length() - 12));
    }

}
